package tech.mhuang.pacebox.core.date;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.IntStream;

/**
 * 时间转换工具类
 *
 * @author mhuang
 * @since 1.0.0
 */
public class DateTimeUtil {

    private static ZoneId zone;

    static {
        zone = ZoneId.systemDefault();
    }


    private DateTimeUtil() {

    }

    /**
     * 将Data转换成JDK8中的LocalDateTime对象
     *
     * @param date 需要转换的Data对象
     * @return LocalDateTime 返回转换后的LocalDateTime对象
     */
    public static LocalDateTime dateToLocalDateTime(Date date) {
        Instant instant = date.toInstant();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 将Data转换成JDK8中的LocalDate对象
     *
     * @param date 需要转换的Data对象
     * @return LocalDate 返回转换后的LocalDate对象
     */
    public static LocalDate dateToLocalDate(Date date) {
        LocalDateTime localDateTime = dateToLocalDateTime(date);
        return localDateTime.toLocalDate();
    }

    /**
     * 将LocalDateTime转换成JDK8中的Date对象
     *
     * @param localDateTime 需要转换的LocalDateTime对象
     * @return Date 返回转换后的Date对象
     */
    public static Date localDateTimeToDate(LocalDateTime localDateTime) {
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * LocalDate转换成Data、时间取00:00:00
     *
     * @param localDate 需要转换的LocalDate
     * @return Date     转换后的Date
     */
    public static Date localDateToStartDate(LocalDate localDate) {
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * LocalDate转换成Data、时间取00:00:00
     *
     * @param localDateStr 需要转换的localDateStr
     * @return Date     转换后的Date
     */
    public static Date localDateToStartDate(String localDateStr) {
        return localDateToStartDate(LocalDate.parse(localDateStr));
    }

    /**
     * LocalDate转换成Data、时间取23:59:59
     *
     * @param localDate 需要转换的LocalDate
     * @return Date     转换后的Date
     */
    public static Date localDateToEndDate(LocalDate localDate) {
        Instant instant = LocalDateTime.of(localDate, LocalTime.MAX).atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * LocalDate转换成Data、时间取23:59:59
     *
     * @param localDateStr 需要转换的localDateStr
     * @return Date     转换后的Date
     */
    public static Date localDateToEndDate(String localDateStr) {
        return localDateToEndDate(LocalDate.parse(localDateStr));
    }

    /**
     * 将字符串按照{@code yyyy-MM-dd HH:mm:ss}转换成LocalDateTime
     *
     * @param dateTimeStr 转换的字符串
     * @return LocalDateTime 转换后的LocalDateTime对象
     */
    public static LocalDateTime parseDateTime(String dateTimeStr) {
        return LocalDateTime.parse(dateTimeStr, DatePattern.NORM_DATETIME_FORMAT);
    }

    /**
     * 将字符串按照{@code yyyy-MM-dd HH:mm:ss}转换成LocalDateTime
     *
     * @param dateTimeStr 转换的字符串
     * @param datePattern 转换规则
     * @return LocalDateTime 转换后的LocalDateTime对象
     * @since 1.1.4
     */
    public static LocalDateTime parseDateTime(String dateTimeStr, String datePattern) {
        return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern(datePattern));
    }

    /**
     * 将字符串按照{@code yyyy-MM-dd HH:mm:ss}转换成LocalDateTime
     *
     * @param dateTimeStr 转换的字符串
     * @param formatter   转换规则
     * @return LocalDateTime 转换后的LocalDateTime对象
     * @since 1.1.4
     */
    public static LocalDateTime parseDateTime(String dateTimeStr, DateTimeFormatter formatter) {
        return LocalDateTime.parse(dateTimeStr, formatter);
    }


    /**
     * 将字符串按照{@code yyyy-MM-dd HH:mm:ss}转成Date对象
     *
     * @param dateTimeStr 转换的字符串
     * @return Date 转换后的Date对象
     */
    public static Date parseDateTimeToDate(String dateTimeStr) {
        LocalDateTime localDateTime = parseDateTime(dateTimeStr);
        return localDateTimeToDate(localDateTime);
    }

    /**
     * 将LocalDateTime对象转换成字符串、格式为:{@code yyyy-MM-dd HH:mm:ss}
     *
     * @param localDateTime 转换的对象
     * @return String 转换后存放的字符串
     */
    public static String fromDateTime(LocalDateTime localDateTime) {
        return localDateTime.format(DatePattern.NORM_DATETIME_FORMAT);
    }

    /**
     * 将LocalDateTime对象转换成字符串、格式可自定义、比如:{@code yyyy-MM-dd HH:mm:ss}
     *
     * @param localDateTime 转换的对象
     * @param formatString  转换的格式
     * @return String 转换后存放的字符串
     */
    public static String fromDateTime(LocalDateTime localDateTime, String formatString) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatString);
        return localDateTime.format(formatter);
    }

    /**
     * 将LocalDateTime对象转换成字符串、格式可自定义、比如:{@code DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss)}
     *
     * @param localDateTime 转换的对象
     * @param formatter     转换的格式类
     * @return String 转换后存放的字符串
     */
    public static String fromDateTime(LocalDateTime localDateTime, DateTimeFormatter formatter) {
        return localDateTime.format(formatter);
    }


    /**
     * 将unit时间戳转换成LocalDateTime对象
     *
     * @param unit 时间戳（毫秒级 13位）
     * @return LocalDateTime 转换后的时间对象
     */
    public static LocalDateTime unitToDateTime(Long unit) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(unit), zone);
    }

    /**
     * 将LocalDateTime转换成unit时间戳、毫秒级
     *
     * @param localDateTime 转换的对象
     * @return long 转换后的时间戳毫秒级
     */
    public static long getTimestampOfDateTime(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return instant.toEpochMilli();
    }

    /**
     * 判断日期是不是在当前周
     *
     * @param date 日期
     * @return 是否是当前周
     */
    public static boolean localDateIsThisWeek(LocalDate date) {
        return ChronoUnit.WEEKS.between(LocalDate.now(), date) == 0;
    }

    /**
     * 获取某天当前一周的日期
     *
     * @param date 某天
     * @return 返回一周的日期
     */
    public static List<LocalDate> localDateToThisWeekList(LocalDate date) {
        List<LocalDate> list = new ArrayList<>();
        LocalDate beginDate = date.with(TemporalAdjusters.previous(java.time.DayOfWeek.SUNDAY)).plusDays(1);
        list.add(beginDate);
        IntStream.range(1, 7).boxed().forEachOrdered(begin -> list.add(beginDate.plusDays(begin)));
        return list;
    }

    /**
     * date转字符串
     *
     * @param date   date
     * @param format format
     * @return 结果集
     * @since 1.1.6
     */
    public static String format(Date date, String format) {
        return fromDateTime(DateTimeUtil.dateToLocalDateTime(date), format);
    }

    /**
     * date转字符串
     *
     * @param date   date
     * @param format format
     * @return 结果集
     * @since 1.1.6
     */
    public static String format(Date date, DateTimeFormatter format) {
        return fromDateTime(DateTimeUtil.dateToLocalDateTime(date), format);
    }


    /**
     * 获取某天当前一个月的日期
     *
     * @param date 某天
     * @return 返回一月的日期
     */
    public static List<LocalDate> localDateToThisMonthList(LocalDate date) {
        int monthDayNum = getMonthDayNumByLocalDate(date) + 1;
        List<LocalDate> list = new ArrayList<>(monthDayNum);
        IntStream.range(1, monthDayNum).boxed().forEachOrdered(begin -> list.add(LocalDate.of(date.getYear(), date.getMonth(), begin)));
        return list;
    }

    /**
     * 获取某日期当月的天数
     *
     * @param date 某日期
     * @return 天数
     */
    public static int getMonthDayNumByLocalDate(LocalDate date) {
        return date.getMonth().length(date.isLeapYear());
    }

    /**
     * 判断开始时间到结束时间相差天数
     *
     * @param before 开始日期
     * @param after  结束日期
     * @return 相差天数, 主要是结束-开始
     */
    public static Long getLocalDateDiffDay(LocalDate before, LocalDate after) {
        return after.toEpochDay() - before.toEpochDay();
    }

    /**
     * 获取当前的Data
     *
     * @return 返回当前的Data对象
     */
    public static Date currentDate() {
        return Date.from(Instant.now());
    }
}
